﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Matériau de rendu d'une réflexion de Phong
    /// </summary>
    [System.Windows.Markup.ContentProperty("Brush")]
    public sealed class SpecularMaterial : Material
    {
        #region DependencyProperties
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="PhongCoefficient"/>.
        /// </summary>
        public static readonly DependencyProperty PhongCoefficientProperty =
            DependencyProperty.Register("PhongCoefficient", typeof(float), typeof(SpecularMaterial),
            new PropertyMetadata(0.5f, new PropertyChangedCallback(PhongCoefficient_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="PhongExponent"/>.
        /// </summary>
        public static readonly DependencyProperty PhongExponentProperty =
            DependencyProperty.Register("PhongExponent", typeof(float), typeof(SpecularMaterial),
            new PropertyMetadata(10.0f, new PropertyChangedCallback(PhongExponent_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="Brush"/>.
        /// </summary>
        public static readonly DependencyProperty BrushProperty =
            DependencyProperty.Register("Brush", typeof(Brush), typeof(SpecularMaterial),
            new PropertyMetadata(new PropertyChangedCallback(Brush_PropertyChanged)));
        #endregion

        #region Dependency Properties Callbacks
        private static void PhongCoefficient_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            (sender as SpecularMaterial).OnPhongCoefficientChanged(e);
        }

        private static void PhongExponent_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            (sender as SpecularMaterial).OnPhongExponentChanged(e);
        }

        private static void Brush_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            (sender as SpecularMaterial).OnBrushChanged(e);
        }
        #endregion

        #region Data
        private const string g_fPhongCoefficient = "g_fPhongCoefficient";
        private const string g_fPhongExponent = "g_fPhongExponent";
        private const string reflection_texture = "reflection_texture";
        #endregion

        #region Public Properties
        /// <summary>
        /// Coefficient de Phong
        /// </summary>
        public float PhongCoefficient
        {
            get { return (float)GetValue(PhongCoefficientProperty); }
            set { SetValue(PhongCoefficientProperty, value); }
        }

        /// <summary>
        /// Exposant de Phong
        /// </summary>
        public float PhongExponent
        {
            get { return (float)GetValue(PhongExponentProperty); }
            set { SetValue(PhongExponentProperty, value); }
        }

        /// <summary>
        /// Masque de réflexion
        /// </summary>
        public Brush Brush
        {
            get { return (Brush)GetValue(BrushProperty); }
            set { SetValue(BrushProperty, value); }
        }
        #endregion
        
        #region Private Members
        private void OnPhongCoefficientChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (!object.ReferenceEquals(this.Effect, null))
                this.Effect.GetVariableByName(g_fPhongCoefficient).AsScalar().Set(PhongCoefficient);
        }

        private void OnPhongExponentChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (!object.ReferenceEquals(this.Effect, null))
                this.Effect.GetVariableByName(g_fPhongExponent).AsScalar().Set(PhongExponent);
        }

        private void OnBrushChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (!object.ReferenceEquals(this.Effect, null))
                this.Effect.GetVariableByName(reflection_texture).AsResource().SetTexture(Brush);
        }
        #endregion

        #region Material Overrides
        /// <summary>
        /// Appelé lors de l'initialisation de l'effet associé
        /// </summary>
        /// <param name="effect">Effet associé</param>
        protected override void OnInitEffect(SlimDX.Direct3D10.Effect effect)
        {
            base.OnInitEffect(effect);
            if (!object.ReferenceEquals(this.Effect, null))
            {
                this.Effect.GetVariableByName(g_fPhongCoefficient).AsScalar().Set(PhongCoefficient);
                this.Effect.GetVariableByName(g_fPhongExponent).AsScalar().Set(PhongExponent);
                this.Effect.GetVariableByName(reflection_texture).AsResource().SetTexture(Brush);
            }
        }
        #endregion

        #region Freezable Implementation
        /// <summary>
        /// Une fois implémentée dans une classe dérivée, initialise une nouvelle instance de la classe dérivée <see cref="Freezable"/>. 
        /// </summary>
        /// <returns>Nouvelle instance.</returns>
        protected override Freezable CreateInstanceCore()
        {
            return new SpecularMaterial();
        }
        #endregion
    }
}
